"""
请求验证中间件
提供请求参数验证、数据清洗和安全检查功能
"""
import re
import json
import html
from typing import Dict, Any, Optional, List, Union
from datetime import datetime
from urllib.parse import urlparse
from loguru import logger

from utils.logger import Logger
from utils.exceptions import APIException, ErrorCode


class ValidationRule:
    """验证规则"""
    
    def __init__(self, rule_type: str, **kwargs):
        self.rule_type = rule_type
        self.kwargs = kwargs
    
    def validate(self, value: Any, field_name: str) -> Any:
        """验证值"""
        if self.rule_type == "required":
            if value is None or value == "":
                raise ValueError(f"字段 {field_name} 是必填项")
            return value
        
        elif self.rule_type == "string":
            if value is not None:
                if not isinstance(value, str):
                    raise ValueError(f"字段 {field_name} 必须是字符串")
                max_length = self.kwargs.get("max_length")
                if max_length and len(value) > max_length:
                    raise ValueError(f"字段 {field_name} 长度不能超过 {max_length}")
                min_length = self.kwargs.get("min_length")
                if min_length and len(value) < min_length:
                    raise ValueError(f"字段 {field_name} 长度不能少于 {min_length}")
            return value
        
        elif self.rule_type == "integer":
            if value is not None:
                try:
                    value = int(value)
                except (ValueError, TypeError):
                    raise ValueError(f"字段 {field_name} 必须是整数")
                
                min_value = self.kwargs.get("min")
                if min_value is not None and value < min_value:
                    raise ValueError(f"字段 {field_name} 不能小于 {min_value}")
                
                max_value = self.kwargs.get("max")
                if max_value is not None and value > max_value:
                    raise ValueError(f"字段 {field_name} 不能大于 {max_value}")
            return value
        
        elif self.rule_type == "float":
            if value is not None:
                try:
                    value = float(value)
                except (ValueError, TypeError):
                    raise ValueError(f"字段 {field_name} 必须是数字")
                
                min_value = self.kwargs.get("min")
                if min_value is not None and value < min_value:
                    raise ValueError(f"字段 {field_name} 不能小于 {min_value}")
                
                max_value = self.kwargs.get("max")
                if max_value is not None and value > max_value:
                    raise ValueError(f"字段 {field_name} 不能大于 {max_value}")
            return value
        
        elif self.rule_type == "email":
            if value is not None and value != "":
                email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
                if not re.match(email_pattern, str(value)):
                    raise ValueError(f"字段 {field_name} 不是有效的邮箱地址")
            return value
        
        elif self.rule_type == "url":
            if value is not None and value != "":
                try:
                    result = urlparse(str(value))
                    if not all([result.scheme, result.netloc]):
                        raise ValueError(f"字段 {field_name} 不是有效的URL")
                except Exception:
                    raise ValueError(f"字段 {field_name} 不是有效的URL")
            return value
        
        elif self.rule_type == "regex":
            if value is not None and value != "":
                pattern = self.kwargs.get("pattern")
                if pattern:
                    if not re.match(pattern, str(value)):
                        message = self.kwargs.get("message", f"字段 {field_name} 格式不正确")
                        raise ValueError(message)
            return value
        
        elif self.rule_type == "in":
            if value is not None:
                allowed_values = self.kwargs.get("values", [])
                if value not in allowed_values:
                    raise ValueError(f"字段 {field_name} 必须是 {allowed_values} 中的一个")
            return value
        
        elif self.rule_type == "custom":
            validator_func = self.kwargs.get("validator")
            if validator_func:
                return validator_func(value, field_name)
            return value
        
        else:
            raise ValueError(f"未知的验证规则类型: {self.rule_type}")


class RequestValidator:
    """请求验证器"""
    
    def __init__(self):
        self.logger = Logger.get_logger(agent_name="request_validator")
        
        # 默认验证规则
        self.default_rules = {
            "user_id": [
                ValidationRule("required"),
                ValidationRule("string", max_length=64)
            ],
            "username": [
                ValidationRule("required"),
                ValidationRule("string", min_length=3, max_length=32),
                ValidationRule("regex", pattern=r'^[a-zA-Z0-9_]+$', 
                             message="用户名只能包含字母、数字和下划线")
            ],
            "email": [
                ValidationRule("required"),
                ValidationRule("email")
            ],
            "password": [
                ValidationRule("required"),
                ValidationRule("string", min_length=8, max_length=128)
            ],
            "page": [
                ValidationRule("integer", min=1, max=10000)
            ],
            "page_size": [
                ValidationRule("integer", min=1, max=100)
            ],
            "search_query": [
                ValidationRule("string", max_length=200)
            ],
            "document_id": [
                ValidationRule("required"),
                ValidationRule("string", max_length=128)
            ],
            "content": [
                ValidationRule("required"),
                ValidationRule("string", max_length=100000)  # 100KB限制
            ]
        }
        
        # 路由特定的验证规则
        self.route_rules = {
            "POST:/api/v1/auth/login": {
                "username": self.default_rules["username"],
                "password": self.default_rules["password"]
            },
            "POST:/api/v1/auth/register": {
                "username": self.default_rules["username"],
                "email": self.default_rules["email"],
                "password": self.default_rules["password"]
            },
            "GET:/api/v1/documents": {
                "page": self.default_rules["page"],
                "page_size": self.default_rules["page_size"],
                "search": self.default_rules["search_query"]
            },
            "POST:/api/v1/documents": {
                "title": [
                    ValidationRule("required"),
                    ValidationRule("string", max_length=200)
                ],
                "content": self.default_rules["content"],
                "tags": [
                    ValidationRule("string", max_length=500)
                ]
            },
            "GET:/api/v1/documents/{document_id}": {
                "document_id": self.default_rules["document_id"]
            }
        }
        
        self.logger.info("请求验证器初始化完成")
    
    async def validate(self, request_data: Dict[str, Any], 
                      route_config: Any) -> Dict[str, Any]:
        """
        验证请求数据
        
        Args:
            request_data: 请求数据
            route_config: 路由配置
            
        Returns:
            验证并清洗后的数据
            
        Raises:
            APIException: 验证失败时
        """
        try:
            # 构建路由键
            route_key = f"{route_config.method}:{route_config.path}"
            
            # 获取验证规则
            rules = self.route_rules.get(route_key, {})
            
            if not rules:
                self.logger.debug(f"没有找到验证规则: {route_key}")
                return request_data
            
            # 验证数据
            validated_data = {}
            errors = []
            
            for field_name, field_rules in rules.items():
                try:
                    value = request_data.get(field_name)
                    
                    # 应用所有验证规则
                    for rule in field_rules:
                        value = rule.validate(value, field_name)
                    
                    # 清洗数据
                    if value is not None:
                        value = self._sanitize_value(value, field_name)
                    
                    validated_data[field_name] = value
                    
                except ValueError as e:
                    errors.append(str(e))
                except Exception as e:
                    errors.append(f"字段 {field_name} 验证失败: {str(e)}")
            
            # 检查是否有错误
            if errors:
                raise APIException(
                    error_code=ErrorCode.INVALID_REQUEST,
                    message="请求参数验证失败",
                    details={"errors": errors}
                )
            
            self.logger.debug(f"验证成功: {route_key}, 字段数: {len(validated_data)}")
            return validated_data
            
        except APIException:
            raise
        except Exception as e:
            self.logger.error(f"验证过程失败: {str(e)}")
            raise APIException(
                error_code=ErrorCode.INTERNAL_ERROR,
                message="请求验证失败"
            )
    
    def _sanitize_value(self, value: Any, field_name: str) -> Any:
        """清洗数据"""
        if isinstance(value, str):
            # HTML转义（防止XSS攻击）
            if field_name in ["content", "title", "description"]:
                value = html.escape(value)
            
            # 移除控制字符
            value = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]', '', value)
            
            # 去除前后空白
            value = value.strip()
            
            # 限制字符串长度
            max_length = self._get_max_length_for_field(field_name)
            if max_length and len(value) > max_length:
                value = value[:max_length]
        
        elif isinstance(value, (list, dict)):
            # 递归清洗复杂类型
            value = self._sanitize_complex_value(value)
        
        return value
    
    def _sanitize_complex_value(self, value: Any) -> Any:
        """清洗复杂类型的值"""
        if isinstance(value, list):
            return [self._sanitize_complex_value(item) for item in value]
        elif isinstance(value, dict):
            return {k: self._sanitize_complex_value(v) for k, v in value.items()}
        elif isinstance(value, str):
            # 基本的字符串清洗
            return html.escape(value.strip())
        else:
            return value
    
    def _get_max_length_for_field(self, field_name: str) -> Optional[int]:
        """获取字段的最大长度限制"""
        field_limits = {
            "username": 32,
            "email": 100,
            "title": 200,
            "search_query": 200,
            "tags": 500,
            "description": 1000
        }
        return field_limits.get(field_name)
    
    def add_validation_rule(self, route_key: str, field_name: str, rules: List[ValidationRule]):
        """添加验证规则"""
        if route_key not in self.route_rules:
            self.route_rules[route_key] = {}
        
        self.route_rules[route_key][field_name] = rules
        self.logger.info(f"添加验证规则: {route_key}.{field_name}")
    
    def validate_email(self, email: str) -> bool:
        """验证邮箱格式"""
        try:
            rule = ValidationRule("email")
            rule.validate(email, "email")
            return True
        except ValueError:
            return False
    
    def validate_password(self, password: str) -> Dict[str, Union[bool, List[str]]]:
        """验证密码强度"""
        errors = []
        
        if len(password) < 8:
            errors.append("密码长度至少为8位")
        
        if not re.search(r'[A-Z]', password):
            errors.append("密码必须包含大写字母")
        
        if not re.search(r'[a-z]', password):
            errors.append("密码必须包含小写字母")
        
        if not re.search(r'\d', password):
            errors.append("密码必须包含数字")
        
        if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
            errors.append("密码必须包含特殊字符")
        
        return {
            "valid": len(errors) == 0,
            "errors": errors
        }
    
    def sanitize_html_content(self, content: str) -> str:
        """清理HTML内容"""
        # 移除危险的HTML标签和属性
        dangerous_tags = [
            'script', 'iframe', 'object', 'embed', 'form', 'input',
            'textarea', 'button', 'select', 'option', 'link', 'meta'
        ]
        
        # 简单的标签移除（生产环境应该使用更完善的HTML清理库）
        for tag in dangerous_tags:
            content = re.sub(f'<{tag}[^>]*>.*?</{tag}>', '', content, flags=re.IGNORECASE | re.DOTALL)
            content = re.sub(f'<{tag}[^>]*/?>', '', content, flags=re.IGNORECASE)
        
        # HTML转义
        content = html.escape(content)
        
        return content